
import time
import random 
from operator import itemgetter

from dexen.user.libs import constants as dc, server_api
from dexen.user.libs.algorithms import ea
from libs import constants, input_reading


""" GLOBALS """
pop_api = server_api.PopAPIFactory()
boxes = input_reading.read_input()
n_boxes = len(boxes)

objectives = [constants.WIDTH, constants.HEIGHT]
focus = 0


def generate_encoding(genotype):
    for i, box_no in enumerate(genotype["gamma_x"]):
        genotype["encoding"][box_no] = dict(x=i)
    for i, box_no in enumerate(genotype["gamma_y"]):
        genotype["encoding"][box_no]['y'] = i    

def mutate(genotype):
    for i in range((n_boxes+19)/20):
        box_no = random.randint(0, n_boxes - 1)
        genotype['orient'][box_no] = 1 - genotype['orient'][box_no]

def generate_child(parent1, parent2):
    child = {
        constants.GENOTYPE : {
            "gamma_x" : range(n_boxes),
            "gamma_y" : range(n_boxes),
            "encoding" : [None] * n_boxes,
            "orient" : [None] * n_boxes
        }
    }

    gen1 = parent1[constants.GENOTYPE]
    gen2 = parent2[constants.GENOTYPE]    
    child_gen = child[constants.GENOTYPE]
    
    for i in range(n_boxes):
        child_gen['gamma_x'][i] = gen1['gamma_x'][i]
        child_gen['gamma_y'][i] = gen1['gamma_y'][i]
        child_gen['orient'][i] = gen1['orient'][i]
    
    left_x = random.randint(0, n_boxes - 2)
    right_x = random.randint(left_x + 1, n_boxes - 1)
    
    neighbor_ids = []
    for i in range(left_x, right_x + 1):
        neighbor_ids.append(gen1['gamma_x'][i])
    
    rows = [] 
    """ This keeps the y coordinates of the ones in the window """
    for i in neighbor_ids:
        rows.append(gen1['encoding'][i]['y'])
    rows.sort()
    
    neighbor_ids.sort(cmp=lambda a,b: gen2['encoding'][a]['x'] - gen2['encoding'][b]['x'])
    cnt = 0
    for i in range(left_x, right_x + 1):
        child_gen['gamma_x'][i] = neighbor_ids[cnt]
        cnt = cnt + 1
    
    neighbor_ids.sort(cmp=lambda a,b: gen2['encoding'][a]['y'] - gen2['encoding'][b]['y'])
    cnt = 0
    for i in rows:
        child_gen['gamma_y'][i] = neighbor_ids[cnt]
        cnt = cnt + 1
    
    generate_encoding(child_gen)
    mutate(child_gen)

    return child
        
def generate_children(parent1, parent2):    
    child1 = generate_child(parent1, parent2)
    child2 = generate_child(parent2, parent1)
    return [child1, child2]

def perturb_parents(individuals):
    shuffle_size = len(individuals) / 10
    pos = random.randint(0, len(individuals) - shuffle_size)
    shuffled_sublist = individuals[pos:pos + shuffle_size]
    random.shuffle(shuffled_sublist)
    individuals[pos:pos + shuffle_size] = shuffled_sublist
    return 


def reproduction(individuals):
    individuals.sort(key = lambda x : x[objectives[focus]])
    perturb_parents(individuals)

    """ Check the population size if it is even """
    new_gen = []    
    for i in range(0, len(individuals) - 1, 2):
        new_gen.extend(generate_children(individuals[i], individuals[i+1]))
    
    if len(individuals) % 2 != 0:
        new_gen.append(generate_child(individuals[-2], individuals[-1]))
    
    return new_gen

def feedback(individuals):  
    print "I am in feedback"  
    
    """
    individuals = pop_api.downloadLiveIndividuals(
        select = (constants.GENOTYPE, constants.WIDTH, constants.HEIGHT, constants.AREA),
        where = (
            (constants.AREA, dc.NOT_EQUAL, None),
        ), 
        count = pop_api.getGlobalData()[constants.FEEDBACK],
    )
    """
    
    for ind in individuals:
        assert isinstance(ind[constants.GENOTYPE], dict)
        assert isinstance(ind, dict)
    
    global focus, objectives
    
    env_selector = ea.EnvironmentalSelection()
    env_selector.setObjectives((constants.WIDTH, dc.MINIMIZE),(constants.HEIGHT, dc.MINIMIZE))
    env_selector.setSelectionRate(0.5)

    deads, alives = env_selector.select(individuals)
    
    for dead_ind in deads:
        pop_api.killIndividual(dead_ind)

    for alive_ind in alives:
        pop_api.reviveIndividual(alive_ind)

    parents = alives
    #assert len(parents) == pop_api.getGlobalData()[constants.FEEDBACK] / 2
    
    new_gen = reproduction(parents)
    focus = 1 - focus
    
    for ind in individuals + new_gen:
        assert isinstance(ind[constants.GENOTYPE], dict)
    
    #pop_api.uploadIndividuals(individuals + new_gen)
    return individuals + new_gen  
